/**
 * 
 */
package com.zhb.cloud.core.distribute.redisson.lock;

import java.util.concurrent.TimeUnit;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zhb.cloud.config.RedisConfig;
import com.zhb.cloud.core.distribute.lock.DistributedLock;
import com.zhb.cloud.core.distribute.lock.DistributedLockException;
import com.zhb.cloud.core.distribute.lock.DistributedLockUtils;

/**
 * @ClassName: RedisDistributedLock
 * @author: zhb
 * @date: 2017年8月4日 下午1:23:55
 * @Description: redis 分布式锁实现
 *
 */
public class RedisDistributedLock implements DistributedLock {
	/**
	 * The logger instance
	 */
	private static final Logger LOG = LoggerFactory.getLogger(RedisDistributedLock.class);

	/**
	 * lock namespace.
	 */
	public static final String NAMESPACE = "/px/locks";

	/**
	 * lock namespace
	 */
	@SuppressWarnings("unused")
	private String namespace;

	/**
	 * Distributed redisson lock
	 */
	private RLock rlock;

	/**
	 * The lock path
	 */
	private String lockPath;

	/**
	 * @param redissonClient
	 *            the redisson client
	 * @param path
	 *            the lock path, such as /example/lock
	 * @param namespace
	 *            the lock namespace
	 */
	public RedisDistributedLock(RedissonClient redissonClient, String path, String namespace) {
		initDistributedLock(redissonClient, path, namespace);
	}

	/**
	 * @param config
	 * @param path
	 */
	public RedisDistributedLock(RedisConfig config, String path) {
		RedissonClient redissonClient = RedisClientFactory.createRedisson(config.host, config.port, config.timeout,
				config.password);
		initDistributedLock(redissonClient, path, NAMESPACE);
	}

	/**
	 * Initialize distributed lock
	 *
	 * @param redissonClient
	 *            the redisson client
	 * @param path
	 *            the path to lock
	 * @param namespace
	 *            the lock namespace
	 */
	private void initDistributedLock(RedissonClient redissonClient, String path, String namespace) {
		if (redissonClient == null) {
			throw new DistributedLockException("No redisson client found");
		}
		DistributedLockUtils.validatePath(path);

		this.namespace = namespace;
		this.lockPath = namespace + path;
		this.rlock = redissonClient.getLock(this.lockPath);

		LOG.info("Initialize redis lock instance, path={} lock={}", this.lockPath, this.rlock);
		if (this.rlock == null) {
			throw new DistributedLockException("create redisson lock failed");
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see cn.px.hq.server.locks.DistributedLock#lock()
	 */
	@Override
	public void lock() throws DistributedLockException {
		try {
			this.rlock.lock();
			LOG.info("{} acquire lock: {}", Thread.currentThread(), lockPath);
		} catch (Exception e) {
			throw new DistributedLockException("", e);
		}
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see cn.px.hq.server.locks.DistributedLock#lock(long, TimeUnit)
	 */
	@Override
	public void lock(long time, TimeUnit unit) throws DistributedLockException {
		try {
			this.rlock.lock(time, unit);
			LOG.info("{} acquire lock: {}", Thread.currentThread(), lockPath);
		} catch (Exception e) {
			throw new DistributedLockException("failed to acquire the lock", e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see cn.px.hq.server.locks.DistributedLock#unlock()
	 */
	@Override
	public void unlock() throws DistributedLockException {
		try {
			this.rlock.unlock();
			LOG.info("{} release lock: {}", Thread.currentThread(), lockPath);
		} catch (Exception e) {
			throw new DistributedLockException("", e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see cn.px.hq.server.locks.DistributedLock#tryLock()
	 */
	@Override
	public boolean tryLock() throws DistributedLockException {
		boolean result = false;
		try {
			result = this.rlock.tryLock();
		} catch (Exception e) {
			throw new DistributedLockException("", e);
		}
		LOG.info("{} try lock: {} result: {}", Thread.currentThread(), lockPath, result);
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see cn.px.hq.server.locks.DistributedLock#tryLock(long,
	 * java.util.concurrent.TimeUnit)
	 */
	@Override
	public boolean tryLock(long time, TimeUnit unit) throws DistributedLockException {
		boolean result = false;
		try {
			result = this.rlock.tryLock(time, unit);
		} catch (Exception e) {
			throw new DistributedLockException("", e);
		}
		LOG.info("{} try lock: {} result: {}", Thread.currentThread(), lockPath, result);
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see cn.px.hq.server.locks.DistributedLock#tryLock(long, long,
	 * java.util.concurrent.TimeUnit)
	 */
	@Override
	public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws DistributedLockException {
		boolean result = false;
		try {
			result = this.rlock.tryLock(waitTime, leaseTime, unit);
		} catch (Exception e) {
			throw new DistributedLockException("", e);
		}
		LOG.info("{} try lock: {} result: {}", Thread.currentThread(), lockPath, result);
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see cn.px.hq.server.locks.DistributedLock#isLocked()
	 */
	@Override
	public boolean isLocked() throws DistributedLockException {
		try {
			return this.rlock.isLocked();
		} catch (Exception e) {
			throw new DistributedLockException("", e);
		}
	}
}
